package com.example.thread;

import com.example.demo.exception.BizException;

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 *  CountDownLatch中count down是倒数的意思，latch则是门闩的含义。整体含义可以理解为倒数的门栓，似乎有一点“三二一，芝麻开门”的感觉。
 * CountDownLatch的作用也是如此，在构造CountDownLatch的时候需要传入一个整数n，
 * 在这个整数“倒数”到0之前，主线程需要等待在门口，而这个“倒数”过程则是由各个执行线程驱动的，每个线程执行完一个任务“倒数”一次。
 * 总结来说，CountDownLatch的作用就是等待其他的线程都执行完任务，必要时可以对各个任务的执行结果进行汇总，然后主线程才继续往下执行。
 *
 *  CountDownLatch主要有两个方法：countDown()和await()。countDown()方法用于使计数器减一，
 * 其一般是执行任务的线程调用，await()方法则使调用该方法的线程处于等待状态，其一般是主线程调用。
 * 这里需要注意的是，countDown()方法并没有规定一个线程只能调用一次，当同一个线程调用多次countDown()方法时，每次都会使计数器减一；
 * 另外，await()方法也并没有规定只能有一个线程执行该方法，如果多个线程同时执行await()方法，
 * 那么这几个线程都将处于等待状态，并且以共享模式享有同一个锁
 */
public class CountDownLatchTest1 {
    public static void main(String[] args) {
        test1(3);
        //test2(3);
    }

    /**
     * lambda 格式
     * @param length
     */
    public static void test1(int length){
        ExecutorService executor = Executors.newFixedThreadPool(length);
        CountDownLatch latch = new CountDownLatch(length);
        for(int i=0; i<length; i++){
            Thread thread = new Thread(() -> {
                try {
                    System.out.println("子线程： " + Thread.currentThread().getName() + "执行前");
                    Thread.sleep(5000);
                    /*if(1==1) {
                        latch.countDown();
                        return;
                    }*/
                    System.out.println("子线程： " + Thread.currentThread().getName() + "执行后");
                    if(1 == 1) {
                        throw new BizException("111");
                    }
                    latch.countDown();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            });
            executor.execute(thread);
        }
        try {
            System.out.println("主线程： " + Thread.currentThread().getName() + "执行前");
            latch.await();
            System.out.println("所有子线程执行完毕，主线程： " + Thread.currentThread().getName() + "继续执行");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        executor.shutdown();
    }

    /**
     * Runnable 格式
     * @param length
     */
    public static void test2(int length){
        ExecutorService executor = Executors.newFixedThreadPool(length);
        CountDownLatch latch = new CountDownLatch(length);
        for(int i=0; i<length; i++){
            Runnable thread = new Runnable(){
                @Override
                public void run() {
                    try {
                        System.out.println("子线程： " + Thread.currentThread().getName() + "执行前");
                        Thread.sleep(5000);
                        System.out.println("子线程： " + Thread.currentThread().getName() + "执行后");
                        latch.countDown();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            };
            executor.execute(thread);
        }
        try {
            System.out.println("主线程： " + Thread.currentThread().getName() + "执行前");
            latch.await();
            System.out.println("所有子线程执行完毕，主线程： " + Thread.currentThread().getName() + "继续执行");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        executor.shutdown();
    }
}
